《Android 基础(十七)》 ViewPager

简介

Layout manager that allows the user to flip left and right
through pages of data. You supply an implementation of a
{@link PagerAdapter} to generate the pages that the view shows.

ViewPager is most often used in conjunction with {@link android.app.Fragment},
which is a convenient way to supply and manage the lifecycle of each page.
There are standard adapters implemented for using fragments with the ViewPager,
which cover the most common use cases. These are
{@link android.support.v4.app.FragmentPagerAdapter} and
{@link android.support.v4.app.FragmentStatePagerAdapter}; each of these
classes have simple code showing how to build a full user interface
with them.

Views which are annotated with the {@link DecorView} annotation are treated as
part of the view pagers ‘decor’. Each decor view’s position can be controlled via
its {@code android:layout_gravity} attribute. For example:

1
2
3
4
5
6
7
8
<android.support.v4.view.ViewPager  
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.v4.view.PagerTitleStrip
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="top" />
</android.support.v4.view.ViewPager>

翻译:
ViewPager允许用户通过载有数据的页面实现左右滑动。开发者需要自己实现PagerAdapter来生成需要显示的页面。
ViewPager通常回合Fragment结合起来使用,这样实现起来比较简单同时也便于管理每个页面的生命周期。Android存在标准的适配器模板针对Fragment和ViewPager结合使用的方式:
android.support.v4.app.FragmentPagerAdapter
android.support.v4.app.FragmentStatePagerAdapter
每个类都有简单的说明,告诉开发者如何通过他们来实现与用户的交互。

被注释成DecorView的视图均可当做ViewPager的一部分,每一个DecorView的位置可以通过 android:layout_gravity属性来控制,例如:

1
2
3
4
5
6
7
8
<android.support.v4.view.ViewPager  
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.v4.view.PagerTitleStrip
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="top" />
</android.support.v4.view.ViewPager>

类结构

这里写图片描述

常用成员的功能介绍

成员 功能
ViewPager() 构造方法
addOnAdapterChangeListener 添加一个OnAdapterChangeListener
removeOnAdapterChangeListener 移除一个OnAdapterChangeListener
setCurrentItem 设置当前Pager的index
addOnePageChangeListener 设置一个页面改变监听器
addOnePageChangeListener 移除一个页面改变监听器
clearOnPageChangeListener 移除所有的页面改变监听器
setPageTransformer 设置页面之间切换的过程中PageTransformer

其他的一些方法从使用程度上来看比较的少见,源码内容大家通过AS都是可以查看到的。

基本使用

简单显示

布局文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="mraz.com.opensourcedemo.MainActivity">

<android.support.v4.view.ViewPager
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/viewpager"
>
</android.support.v4.view.ViewPager>
</RelativeLayout>

代码内容
MainActivity.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
package mraz.com.opensourcedemo;

import android.support.v4.view.ViewPager;
import android.support.v4.widget.ViewDragHelper;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.view.Window;

import java.util.ArrayList;

public class MainActivity extends AppCompatActivity {

ArrayList<String> sourceList;
private static final float MIN_SCALE = 0.5f;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);
ViewPager viewPager = (ViewPager) findViewById(R.id.viewpager);
MyPagerAdapter myPagerAdapter = new MyPagerAdapter(getSupportFragmentManager());
viewPager.setAdapter(myPagerAdapter);

}

}

Adapter类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
package mraz.com.opensourcedemo;

import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentPagerAdapter;

/**
* Created by Mraz on 2016/7/8.
*/
public class MyPagerAdapter extends FragmentPagerAdapter {

public static final String[] titles = {"Android"," Pet","Gift"};

public MyPagerAdapter(FragmentManager fm) {
super(fm);
}

@Override
public Fragment getItem(int position) {
switch (position) {
case 0:
return new FirstFragment();
case 1:
return new SecondFragment();
case 2:
return new ThreeFragment();
}
return null;
}

@Override
public int getCount() {
return 3;
}
}

MyPagerAdapter 继承FragmentPagerAdapter,当创建这个类并继承FragmentPagerAdapter的时候,AS会提示开发者需要实现两个方法,并且要添加一个构造函数,getItem就是根据位置返回对应的Fragment,getCount就是返回页面数,这里我是直接返回的对应的内容,数目也是固定的,在实际开发过程中,使用ArrayList等数据结构来传递合适的参数,可以更灵活的设置Adapter的内容,使ViewPager的内容更丰富。这里的 FirstFragment,SecondFragment,ThreeFragment内容只是一个简单的图片资源,对应的布局文件也比较简单,不贴代码了。

实际效果

这里写图片描述

添加PageTransformer

代码内容

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
private static final float MIN_SCALE = 0.5f;
...
viewPager.setPageTransformer(true, new ViewPager.PageTransformer() {
@Override
public void transformPage(View view, float position) {

int pageWidth = view.getWidth();

if (position < -1) { // [-Infinity,-1)
// This page is way off-screen to the left.
view.setAlpha(0);

} else if (position <= 0) { // [-1,0]
// Use the default slide transition when moving to the left page
view.setAlpha(1 + position);
view.setTranslationX(0);

float scaleFactor = MIN_SCALE
+ (1 - MIN_SCALE) * (1 - Math.abs(position));
view.setScaleX(scaleFactor);
view.setScaleY(scaleFactor);

} else if (position <= 1) { // (0,1]
// Fade the page out.
view.setAlpha(1 - position);

// Counteract the default slide transition
view.setTranslationX(pageWidth * -position);

// Scale the page down (between MIN_SCALE and 1)
float scaleFactor = MIN_SCALE
+ (1 - MIN_SCALE) * (1 - Math.abs(position));
view.setScaleX(scaleFactor);
view.setScaleY(scaleFactor);

} else { // (1,+Infinity]
// This page is way off-screen to the right.
view.setAlpha(0);
}
}
});

上面是参照例子写的一个页面切换效果,主要关注的是透明度和大小的改变,在页面切换的过程中,对于离开的页面变透明,变小,进入的页面变明显,变大。看下ViewPager.PageTransformer这个接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/**
* A PageTransformer is invoked whenever a visible/attached page is scrolled.
* This offers an opportunity for the application to apply a custom transformation
* to the page views using animation properties.
*
* <p>As property animation is only supported as of Android 3.0 and forward,
* setting a PageTransformer on a ViewPager on earlier platform versions will
* be ignored.</p>
*/
public interface PageTransformer {
/**
* Apply a property transformation to the given page.
*
* @param page Apply the transformation to this page
* @param position Position of page relative to the current front-and-center
* position of the pager. 0 is front and center. 1 is one full
* page position to the right, and -1 is one page position to the left.
*/
public void transformPage(View page, float position);
}

只需要实现一个方法transformPage,传入的参数,第一个参数是页面视图,第二个参数很重要,指的是当前页面所处的一种状态,根据position来判断,我们添加的所有效果,都是要依据这个值来做处理,当我们滑动页面的时候,这个值在不停的变化,并且不同的page的值不相同。

取值 含义
[-Infinity,-1) 这个范围的视图已经看不见了
[-1,0] 即将退出界面的page的变化范围,从0慢慢变成-1
[0,1] 即将进入界面的page的变化范围,从1慢慢变成0
(1,+Infinity] 这个范围的视图已经看不见了

可以把这个取值范围和数据概念——数轴——联系起来,就比较容易理解了,通过上面这个过场和下面的实际效果图,应该可以看出一二,通过这个例子,也可以尝试着实现自定义的过场效果

实际效果

这里写图片描述

添加OnPageChangeListener

代码内容

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
viewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
Log.e(TAG, "onPageScrolled position = " + position + " positionOffset = " + positionOffset + " positionOffsetPixels = " + positionOffsetPixels);
}

@Override
public void onPageSelected(int position) {
Log.e(TAG, "onPageSelected position = " + position);
}

@Override
public void onPageScrollStateChanged(int state) {
Log.e(TAG, "onPageScrollStateChanged state = " + state);
}
});

可以看到当滑动页面的时候,会有一大串的Log打印出来 这里写图片描述 看下OnPagerChangeListener这个接口的定义

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
/**
* Callback interface for responding to changing state of the selected page.
*/
public interface OnPageChangeListener {

/**
* This method will be invoked when the current page is scrolled, either as part
* of a programmatically initiated smooth scroll or a user initiated touch scroll.
*
* @param position Position index of the first page currently being displayed.
* Page position+1 will be visible if positionOffset is nonzero.
* @param positionOffset Value from [0, 1) indicating the offset from the page at position.
* @param positionOffsetPixels Value in pixels indicating the offset from position.
*/
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels);

/**
* This method will be invoked when a new page becomes selected. Animation is not
* necessarily complete.
*
* @param position Position index of the new selected page.
*/
public void onPageSelected(int position);

/**
* Called when the scroll state changes. Useful for discovering when the user
* begins dragging, when the pager is automatically settling to the current page,
* or when it is fully stopped/idle.
*
* @param state The new scroll state.
* @see ViewPager#SCROLL_STATE_IDLE
* @see ViewPager#SCROLL_STATE_DRAGGING
* @see ViewPager#SCROLL_STATE_SETTLING
*/
public void onPageScrollStateChanged(int state);
}

值得的一提的是 positionOffset 这个参数可以在onPageScrolled回调中拿到,而这个数值呢 就是和我们上面自定义过场的时候用到的 transformPage方法中传入的第二个参数position 有联系,只是这里的positionOffset取值范围为[0, 1),上面的position中取值范围更广,但是从实际上来看,对我们影响比较大的就是当前切换的两个Page,也就是[-1,1]这个取值范围,所谓的负值可以理解成是相对概念,所以,这两个变量之间是存在关系的,如果不理解上面PageTransformer, 可以结合这个positionOffset变量的打印值加以理解。

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×